package com.github.limit.util;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;

/**
 * @author: wangdejun
 * @create: 2019-11-12 18:08
 **/
public class ParamUtils {

    public static <T> T parseExpression(String expression, JoinPoint joinPoint, Class<T> classType) {
        Method method = getMethod(joinPoint);
        return parseExpression(expression, method, joinPoint.getArgs(), classType);
    }

    public static <T> T parseExpression(String expression, Method method, Object[] args, Class<T> classType) {
        if (StringUtils.isBlank(expression)) {
            return null;
        } else if (!expression.trim().startsWith("#") && !expression.trim().startsWith("$")) {
            return null;
        } else {
            LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = discoverer.getParameterNames(method);
            if (paramNames == null || paramNames.length == 0) {
                return null;
            } else {
                StandardEvaluationContext context = new StandardEvaluationContext();

                for (int i = 0; i < paramNames.length; ++i) {
                    context.setVariable(paramNames[i], args[i]);
                }
                return (new SpelExpressionParser()).parseExpression(expression).getValue(context, classType);
            }
        }
    }

    private static Method getMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        if (method.getDeclaringClass().isInterface()) {
            try {
                method = joinPoint
                        .getTarget()
                        .getClass()
                        .getDeclaredMethod(joinPoint.getSignature().getName(),
                                method.getParameterTypes());
            } catch (SecurityException | NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }
        return method;
    }
}
